package com.spring.service.impl;

import com.github.benmanes.caffeine.cache.Cache;
import com.spring.dao.ArticleDao;
import com.spring.dao.ArticleTagDao;
import com.spring.dao.TagDao;
import com.spring.pojo.Article;
import com.spring.service.ArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;


@Transactional(readOnly = false,//是否是只读事务
               timeout = 60,//事务超时时间(秒)
               rollbackFor = Exception.class,//出现什么异常进行回滚
               isolation = Isolation.READ_COMMITTED,//事务隔离级别
               propagation = Propagation.REQUIRES_NEW)//事务传播特性propagation
@Slf4j
@Service
public class ArticleServiceImpl implements ArticleService {
    //private static final Logger log=
      //      LoggerFactory.getLogger(ArticleServiceImpl.class);
    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private ArticleTagDao articleTagDao;

    /**
     * 此注解描述的方法为一个事务切入点方法，底层会基于AOP方式进行事务控制，
     * 底层会将事务控制逻辑放到一些通知方法中，然后在通知方法内部进行事务逻辑编写，
     * 例如开启事务、提交事务、回滚事务等。
     * 说明：使用注解描述业务层方法，然后启动事务，进行事务控制的方式，我们称之为
     * 声明式事务控制。
     * @param article
     */
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void insert(Article article) {
        articleDao.insert(article);
        articleTagDao.insert(article.getId(),null);
        log.info("article send ok ");
    }

    @Transactional(readOnly = true)
    @Override
    public Article selectById(Long id) {
        return articleDao.selectById(id);
    }


    @Autowired
    private Cache<String,Object> caffeineCache;
    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional(readOnly = true)
    @Override
    public List<Article> list() {
        //1.查本地缓存Caffeine
        List<Article> list=( List<Article>)caffeineCache.asMap().get("articleList");
        if(list!=null) return list;
        //2.查分布式缓存redis
        list = (List<Article>)redisTemplate.opsForValue().get("articleList");
        if(list!=null){
            caffeineCache.asMap().put("articleList", list);
            return list;
        }
        System.out.println("Get Data By ArticleDao");
        //3.从缓存取，缓存没有则调用数据层方法查询数据
        list = articleDao.list();
        //4.将查询到的数据存储到缓存;
        //4.1向本地缓存Caffeine存一份
        caffeineCache.put("articleList", list);
        //4.2向redis分布式缓存存储一份
        redisTemplate.opsForValue().set(
                "articleList",
                list,
                120,//过期时间
                TimeUnit.SECONDS);//时间单位
        return list;
    }
}
